
#include<vector>

#include <errno.h>
#include <sys/iomsg.h>
#include <sys/iofunc.h>
#include <sys/dispatch.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "dataControl.h"

#include "../m_connection_tests/komunikacja1.2.h"

using namespace std;

Connector* Connector::instance = NULL;

static sem_t mutexConns;

Connector * Connector::getInstance()
{
	if ( !instance ) {
		instance = new Connector();
	}
	return instance;
}

Connector::Connector()
{
	connName = new char[CONN_NAME_LENGTH];
	connName[0] = 'a';
	for( int i = 0; i < CONN_NAME_LENGTH; i++)
		connName[i] = '\0';
	strcpy(connName, "NO_NAME");
	
	connectionsOpened = new std::vector<ConnFD>();
	
	sem_init( &mutexConns, 0, 1);
}

void Connector::createListener( char * _connLocalName)
{
	
	/** zapisuje nazwe naszego kanalu**/
	strcpy(connName, _connLocalName);

	/** watek, ktory bedzie nasluchiwal**/	
	pthread_attr_t attr;

   	pthread_attr_init( &attr );
   	pthread_attr_setdetachstate(
      		&attr, PTHREAD_CREATE_DETACHED );
      		
	pthread_create( NULL, &attr, &run , _connLocalName );
}

 int  Connector::isConnected( char * arg)
 {
 	int i;
	
 	for(i = 0; i < connectionsOpened->size() ; i++) {
			if(  strcmp(     (* connectionsOpened)[i].connName , arg)== 0) {
			//	cout << "takie same : "<< arg << " i : " <<(* connectionsOpened)[i].connName <<endl;
				return  (* connectionsOpened)[i].fd;
			}
	}
	
	return -1;
 }

ConnFD::ConnFD(  char * name, int _fd)
{
	fd=  _fd;
	connName = name;
}

void  Connector::addConn( char * arg, int fd)
{
	char * par = new char[30];
	strcpy(par, arg);
	
	//cout << "adds connName : " << arg<<endl;
	connectionsOpened->push_back( *(new ConnFD( par, fd)));		
}
 
 
void  Connector::closeAllConns( void )
{
	
	int i, j;
	vector <ConnFD>::iterator iter;
	
	sem_wait(&mutexConns);
	
	iter = connectionsOpened->begin();
	i = 	connectionsOpened->size();
	for( j = 0; j < i; j++) {
		name_close( (*iter).fd);
		connectionsOpened->erase(iter);
		iter = connectionsOpened->begin();
	}
//	cout << "closeAllConns . polaczen jest  : "<< 	connectionsOpened->size() << endl;
	
	sem_post(&mutexConns);
}

/*** Client Side of the code ***/
int sendMessage(  Message arg) {

    my_data_t * msg = &arg.data;
    int fd;
    
    sem_wait(&mutexConns);
	
	if( (fd = Connector::getInstance()->isConnected(arg.getConnReceiverName()))==-1 ) {
    	if ((fd = name_open (arg.getConnReceiverName(), NAME_FLAG_ATTACH_GLOBAL)) == -1) {
    		cout <<"\nERROR sendMessage. pewnie nie ma " <<arg.getConnReceiverName() <<" albo gns -c zapomniales\n";
    	 	cout <<endl <<strerror(errno) << endl ;
    	 	sem_post(&mutexConns);
        	return EXIT_FAILURE;
    	}
	
    	Connector::getInstance()->addConn(arg.getConnReceiverName(), fd);
    } 

    /* We would have pre-defined data to stuff here */
    (*msg).hdr.type = 0x00;
    (*msg).hdr.subtype = 0x00;

     if (MsgSend(fd, &(*msg), sizeof(*msg), NULL, 0) == -1) {
  		cout <<"\nsendMessage ERROR1 destination\n";
    	cout <<endl <<strerror(errno) << endl ;
    	sem_post(&mutexConns);
        return EXIT_FAILURE;
    }
   
    if( (fd = Connector::getInstance()->isConnected("GOOFY"))==-1 ) {
		if ((fd = name_open ( "GOOFY", NAME_FLAG_ATTACH_GLOBAL)) == -1) {
	  		cout <<"\nERROR sendMessage. pewnie nie ma goofy albo gns -c zapomniales\n";
	   		cout<< endl<<strerror(errno)<<endl;
	   		sem_post(&mutexConns);
        	return EXIT_FAILURE;
    	}
    	Connector::getInstance()->addConn("GOOFY", fd);
   	}
 
    /* We would have pre-defined data to stuff here */
    (*msg).hdr.type = 0x00;
    (*msg).hdr.subtype = 0x00;

  
    if (MsgSend(fd, &(*msg), sizeof(*msg), NULL, 0) == -1) {
    	cout <<endl<<"sendMessage ERROR2"<<endl;
      	cout<< endl<<strerror(errno)<<endl;
      	sem_post(&mutexConns);
        return EXIT_FAILURE;
    }

	sem_post(&mutexConns);

    return EXIT_SUCCESS;
}





/**
	Funkcja zajmujaca sie komunikatem, ktory przyszedl przez siec
**/
void * messageReceived( void * arg)
{
	Message * msg = (Message *)arg;


	std::vector<lotnisko> airportsEvents;
	std::vector<samolot> planesEvents;
	std::vector<zdarzenie> eventsEvents;
	std::vector<lotnisko_dla_samolotu> airportsPlane;
	
	lotnisko airportAI;
	samolot_dla_lotniska planeAI;
	samolot_dla_samolotu planePL;
		
	unsigned int i, ctr;

	Message * replyMessage = new Message();
	
	replyMessage->setConnReceiverName( msg->getConnSenderName());
	replyMessage->setConnSenderName( msg->getConnReceiverName());	
	//replyMessage->setRemotePid( msg->getRemotePid());
	switch( msg->getType()) {
		case GET_AIRPORTS_EV_DB :	
			airportsEvents = pobierz_lotniska();
			
			replyMessage->setType( RES_GET_AIRPORTS_EV_DB);
			replyMessage->setMessageLength( airportsEvents.size() );
				
			for( i = 0 ; i <  airportsEvents.size() ; i++)
			{
							cout << "RES_GET_AIRPORTS_EV_DB. send " <<endl;
							replyMessage->setNr(i+1);	
							replyMessage->setIntArg1( airportsEvents[i].id_lotniska);
							replyMessage->setIntArg2( airportsEvents[i].pojemnosc);
							replyMessage->setIntArg3( airportsEvents[i].ilosc_szerokich_pasow);
							replyMessage->setIntArg4( airportsEvents[i].ilosc_waskich_pasow);
							replyMessage->setBoolArg1( airportsEvents[i].aktywnosc);
							replyMessage->setFloatArg1( airportsEvents[i].szerokosc_geo);
							replyMessage->setFloatArg2( airportsEvents[i].dlugosc_geo);
							replyMessage->setStringArg1( (char *)airportsEvents[i].nazwa.c_str());
							
							sendMessage( *replyMessage);
						
			}
			break;
			
		case GET_PLANES_EV_DB :
			planesEvents = pobierz_samoloty();			
			replyMessage->setType( RES_GET_PLANES_EV_DB);
			replyMessage->setMessageLength( planesEvents.size() );
			for( i = 0 ; i <  planesEvents.size() ; i++)
			{
							cout << "RES_GET_PLANES_EV_DB. send " <<endl;
							replyMessage->setNr(i+1);	
							replyMessage->setStringArg1( (char *)planesEvents[i].nazwa.c_str());
							replyMessage->setFloatArg1( planesEvents[i].bak);
							replyMessage->setFloatArg2( planesEvents[i].spalanie);
							replyMessage->setFloatArg3( planesEvents[i].predkosc);
							replyMessage->setIntArg1( planesEvents[i].czas_obslugi);
							replyMessage->setBoolArg1( planesEvents[i].czy_szeroki);
							//replyMessage->dumpMessage();
							
							sendMessage( *replyMessage);

			}
			break;

		case GET_EVENTS_EV_DB :
			eventsEvents = pobierz_zdarzenia();			
			replyMessage->setType( RES_GET_EVENTS_EV_DB);
			replyMessage->setMessageLength( eventsEvents.size() );
			for( i = 0 ; i <  eventsEvents.size() ; i++)
			{
							cout << "RES_GET_EVENTS_EV_DB. send " <<endl;
							replyMessage->setNr(i+1);	
							replyMessage->setStringArg1( (char *)eventsEvents[i].nazwa.c_str());
							replyMessage->setIntArg1( eventsEvents[i].typ);
							replyMessage->setIntArg2( eventsEvents[i].podtyp);
							sendMessage( *replyMessage);
			}
			break;

		case MSG_PODAJ_LICZBE_LOTNISK :
			airportsPlane = pobierz_pozycje();
			replyMessage->setType(MSG_LAP_LICZBE_LOTNISK);
			cout << "MSG_LAP_LICZBE_LOTNISK. send " <<endl;
			/**
			for( i = 0, ctr = 0; i < airportsPlane.size(); i++)
			{
				if( airportsPlane[i].aktywnosc == true)
					++ctr;
			}**/
			//replyMessage->setIntArg1( ctr);			
			replyMessage->setIntArg1(airportsPlane.size());
			sendMessage( *replyMessage);
		
			break;

		case  MSG_PRZESLIJ_LOTNISKA :
			cout <<"MSG_LAP_WSPOLRZEDNE_LOTNISKA. send " <<endl;
			airportsPlane = pobierz_pozycje();
			replyMessage->setType(MSG_LAP_WSPOLRZEDNE_LOTNISKA);
			replyMessage->setMessageLength( airportsPlane.size() );
			for( i = 0 ; i < airportsPlane.size() ; i++)
			{
							cout << "MSG_LAP_WSPOLRZEDNE_LOTNISKA. send " <<endl;
							replyMessage->setNr(i+1);
							replyMessage->setIntArg1( airportsPlane[i].id_lotniska);
							replyMessage->setFloatArg1( airportsPlane[i].szerokosc_geo);
							replyMessage->setFloatArg2( airportsPlane[i].dlugosc_geo);
							replyMessage->setStringArg1( (char*)airportsPlane[i].nazwa.c_str());
							sendMessage( *replyMessage);
			}
			break;
						
		case MSG_ZAPODAJ_DANE_TECHNICZNE :
			cout << "MSG_ZAPODAJ_DANE_TECHNICZNE. send " <<endl;
			planePL = techniczne_po_nazwie( msg->getStringArg1());
			replyMessage->setType(MSG_RES_ZAPODAJ_DANE_TECHNICZNE);
			replyMessage->setFloatArg1( planePL.bak);
			replyMessage->setFloatArg2( planePL.spalanie);
			replyMessage->setFloatArg3( planePL.predkosc);

			sendMessage( *replyMessage);
			break;			

		case GET_AIRPORT_AI_DB :
			airportAI = lotnisko_po_id( msg->getIntArg1());			
			replyMessage->setType( RES_GET_AIRPORT_AI_DB);
			cout << "RES_GET_AIRPORT_AI_DB. send " <<endl;

			replyMessage->setStringArg1( (char *)airportAI.nazwa.c_str());
			replyMessage->setIntArg1( airportAI.pojemnosc);
			replyMessage->setFloatArg1( airportAI.szerokosc_geo);
			replyMessage->setFloatArg2( airportAI.dlugosc_geo);
			replyMessage->setIntArg2( airportAI.ilosc_szerokich_pasow);
			replyMessage->setIntArg3( airportAI.ilosc_waskich_pasow);
			replyMessage->setIntArg4( airportAI.id_lotniska);
			replyMessage->setBoolArg1( airportAI.aktywnosc);
			sendMessage( *replyMessage);
		
			break;	
			
		case GET_PLANE_AI_DB :
			cout << "RES_GET_PLANE_AI_DB. send " <<endl;
			
			planeAI = obsluga_po_nazwie( msg->getStringArg1());	
			replyMessage->setType( RES_GET_PLANE_AI_DB);
			replyMessage->setIntArg1( planeAI.czas_obslugi);
			replyMessage->setBoolArg1( planeAI.czy_szeroki);
			sendMessage( *replyMessage);
			break;		
			
		case ACTIVATE_AIRPORT :
			cout << " ACTIVATE_AIRPORT. send " <<endl;
			aktywuj_lotnisko(msg->getIntArg1() );
			break;		
			
		case DISACTIVATE_AIRPORT :
			cout << " DISACTIVATE_AIRPORT. send " <<endl;
			deaktywuj_lotnisko( msg->getIntArg1() );
			break;		
			
		default : break;
		}	

}


void messageHandler( Message  * msg)
{

	/* watek, ktory bedzie nasluchiwal*/
	pthread_attr_t attr;

   	pthread_attr_init( &attr );
   	pthread_attr_setdetachstate(
      		&attr, PTHREAD_CREATE_DETACHED );
      		
	pthread_create( NULL, &attr, &messageReceived, msg );
}


/**
	Funkcja obslugujaca nasluchiwanie na zadania z sieci
**/
void* run( void * arg) {

   name_attach_t *attach;
   
   Message msg;
   Message * msgProper;
   int rcvid;

	struct _msg_info info;
	
   /* Create a local name (/dev/name/local/...) */
   if ((attach = name_attach(NULL, (char * ) arg, NAME_FLAG_ATTACH_GLOBAL)) == NULL) {
   	cout << "ERROR,  run: #"<< strerror(errno) << endl;
       return (void*)EXIT_FAILURE;
   }

   /* Do your MsgReceive's here now with the chid */
   while (1) {
	 rcvid = MsgReceive(attach->chid, &(msg.data), sizeof(msg.data), &info);

       if (rcvid == -1) {/* Error condition, exit */
           break;
       }

       if (rcvid == 0) {/* Pulse received */
           switch (msg.data.hdr.code) {
           case _PULSE_CODE_DISCONNECT:
               /*
                * A client disconnected all its connections (called
                * name_close() for each name_open() of our name) or
                * terminated
                */
             //  Connector::getInstance()->deleteConn( info.pid);   
               Connector::getInstance()->closeAllConns();   
               ConnectDetach(msg.data.hdr.scoid);
               break;
           case _PULSE_CODE_UNBLOCK:
               /*
                * REPLY blocked client wants to unblock (was hit by
                * a signal or timed out).  It's up to you if you
                * reply now or later.
                */
               break;
           
           }
           continue;
       }


       /* name_open() sends a connect message, must EOK this */
       if (msg.data.hdr.type == _IO_CONNECT ) {
           MsgReply( rcvid, EOK, NULL, 0 );
           continue;
       }

       /* Some other QNX IO message was received; reject it */
       if (msg.data.hdr.type > _IO_BASE && msg.data.hdr.type <= _IO_MAX ) {
           MsgError( rcvid, ENOSYS );
           continue;
       }
     
    	MsgReply(rcvid, EOK, 0, 0);
		msgProper = new Message();
		msg.copyMessage(msgProper);
	//	msgProper->setRemotePid( info.pid );    
       messageHandler(msgProper);

   }

   /* Remove the name from the space */
   name_detach(attach, 0);

   return (void*)EXIT_SUCCESS;
}


